/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include "ScanMethod6Syn.h"

//---------------------------------------------------------------------------

ScanMethod6Syn::ScanMethod6Syn(void)
{
	myType = __wmt6Syn;
}

ScanMethod6Syn::~ScanMethod6Syn(void)
{
}

//---------------------------------------------------------------------------
int ScanMethod6Syn::InternalInit(void)
{
	int res;

	res = BuildPortList(true);
	if(res!=MSR_ERR_OK)
		return res;
	
	FillRandomBuffer();
	
	if(workPhase==__wptDiscovery) return MSR_ERR_OK;
	if(workPhase==__wptScan)      return MSR_ERR_OK;

	return MSR_ERR_PHASE;
}

//--------------------------------------------------------------------

int ScanMethod6Syn::InternalExecute(void)
{
	ScanPacket* pack;

	if(addrList==NULL) return MSR_ERR_PARAMETER;

	ScanPair sp;
	while(GetNextPair(&sp, __sptIpAndPort) && !stopStopStop){
		if(PacketsUsed()>0){
			CheckResponses();
			CheckDeaths();
		}

		if(sp.addr!=NULL){
			if(!sp.addr->addrIPv6.IsEmpty()){
				if(workPhase==__wptDiscovery && sp.addr->alive){
					// it's the discovery phase and a previous discovery method has found it already
					// so skip this one
				}
				else{
					if(config->xMinDelay>0){
						if(lastScanTime!=0){
							waitDone = false;
							while(!waitDone){
								CheckResponses();
								SleepEx(1, TRUE);
								scanTime = GetTickCount();
								scanTime = ComputeDeltaTime(lastScanTime, scanTime);
								if(scanTime>=config->xMinDelay)
									waitDone = true;
							}
						}
						lastScanTime = GetTickCount();
					}

					pack = FindFirstUnusedPacket();
					while(pack==NULL){
						SleepEx(waitTime, TRUE);
						CheckResponses();
						CheckDeaths();
						pack = FindFirstUnusedPacket();
					}

					if(pack!=NULL){
						pack->Initialize(true);
						pack->sTime       = GetTickCount();
						sp.addr->addrIPv6.Get(&pack->addr6);
						pack->port        = sp.port;
						pack->methodType  = myType;
						pack->fullAddress = sp.addr;
						pack->machineInfo = FindMachineByIp6(&(pack->addr6));

						if(!SendSyn(sp.addr, sp.port, pack))
							pack->Initialize(false);

						if((packSent % notifTcp) == 0 && packSent>0){
							if(hWnd!=NULL)
								PostMessage(hWnd, CHS_MSG_PACKETS_SENT, 0, (LPARAM)packSent);
						}
					}
				}
			}
		}
	}

	// wait until the end
	while(PacketsUsed()>0){
		CheckResponses();
		CheckDeaths();
		SleepEx(1, TRUE);
	}

	CString str;
	str.Format(_T("Sent: %d, Received: %d, Expired: %d\n"), packSent, packReceived, packExpired);
	OutputDebugString(str);
	// TODO Put this to display the results on screen ! (vezi IPv4)

	return MSR_ERR_OK;
}

//--------------------------------------------------------------------

void ScanMethod6Syn::ComputeRandomValues(void)
{
	rndTcpSeq = GetRandomValue();
}

bool ScanMethod6Syn::SendSyn(MFullAddress* fullAddrIn, WORD portIn, ScanPacket* packIn)
{
	BYTE srcMAC[6];
	in_addr6 srcIPv6;
	bool res;

	if(fullAddrIn==NULL || packIn==NULL){
		// nothing to send
		return false;
	}

	ComputeRandomValues();

	winPcap->FillSrcMAC(&srcMAC[0]);
	winPcap->GetIPv6UnicastAddress(&srcIPv6);

	res = packetBuilderEx6->BuildPackTcpSyn(
		&srcIPv6,
		&(packIn->addr6),
		portIn,
		rndTcpSeq);
	if(!res) return false;

	// add 1 to the sequence to compare it later with received ack
	rndTcpSeq = htonl(rndTcpSeq) + 1;
	packIn->paramDW = ntohl(rndTcpSeq);

	if(!packetBuilderEx6->AddEthernetHeader(srcMAC, fullAddrIn->addrMAC.addr))
		return false;

	return SendPacket(packetBuilderEx6->GetPacket(), (int)packetBuilderEx6->GetPacketSize());
}

bool ScanMethod6Syn::SendAck(in_addr6 *dstAddrIn, WORD dstPortIn, WORD srcPortIn, DWORD seqIn, DWORD ackIn, BYTE* dstMAC)
{
	BYTE srcMAC[6];
	in_addr6 srcIPv6;

	winPcap->FillSrcMAC(&srcMAC[0]);
	winPcap->GetIPv6UnicastAddress(&srcIPv6);

	if(!packetBuilderEx6->BuildPackTcpFlatWithSrcPort(
		&srcIPv6,
		dstAddrIn,
		srcPortIn,
		dstPortIn,
		seqIn,
		ackIn,
		TH_ACK))
		return false;

	if(!packetBuilderEx6->AddEthernetHeader(srcMAC, dstMAC))
		return false;

	return SendPacket(packetBuilderEx6->GetPacket(), (int)packetBuilderEx6->GetPacketSize());
}

bool ScanMethod6Syn::SendRst(in_addr6 *dstAddrIn, WORD dstPortIn, WORD srcPortIn, DWORD seqIn, DWORD ackIn, BYTE* dstMAC)
{
	BYTE srcMAC[6];
	in_addr6 srcIPv6;

	winPcap->FillSrcMAC(&srcMAC[0]);
	winPcap->GetIPv6UnicastAddress(&srcIPv6);

	if(!packetBuilderEx6->BuildPackTcpFlatWithSrcPort(
		&srcIPv6,
		dstAddrIn,
		srcPortIn,
		dstPortIn,
		seqIn,
		ackIn,
		TH_RST))
		return false;

	if(!packetBuilderEx6->AddEthernetHeader(srcMAC, dstMAC))
		return false;

	return SendPacket(packetBuilderEx6->GetPacket(), (int)packetBuilderEx6->GetPacketSize());
}

//--------------------------------------------------------------------

void ScanMethod6Syn::ProcessTcp6(BYTE* dataIn, int sizeIn, BYTE* srcIP, BYTE* srcMAC)
{
	TCPHeaderPG  *tcph;
	ScanPacket   *pack;
	in_addr6     ia6;
	MFullAddress *addr;
	MMachineInfo *mach;

	if(sizeIn<sizeof(TCPHeaderPG)) return;

	tcph = (TCPHeaderPG*)dataIn;

	if(!(tcph->Flags & TH_ACK)) return;

	memcpy(ia6.u.Byte, srcIP, 16);

	if(tcph->Flags & TH_RST){
		// the port is closed
		pack = FindPacket6(&ia6, ntohs(tcph->SourcePort), myType);
		if(pack!=NULL){
			packReceived++;
			pack->Initialize(false);
		}
		return;
	}

	if(tcph->Flags & TH_SYN){
		// the port is open
		MPortInfo mpi;

		pack = FindPacket6(&ia6, ntohs(tcph->SourcePort), myType);
		if(pack!=NULL){
			if(tcph->Ack==pack->paramDW){
				// it is 'my' response
				packReceived++;

				// connection state is ESTABLISHED if you send this
				//DWORD newAck;
				//newAck = tcph->Seq + 1;
				//SendAck(inAddr.S_un.S_addr,
				//	ntohs(tcph->SourcePort), ntohs(tcph->DestinationPort),
				//	tcph->Ack, newAck,
				//	srcMAC);

				// close the connection correctly 
				SendRst(&ia6,
					ntohs(tcph->SourcePort), ntohs(tcph->DestinationPort),
					tcph->Ack, 0x00000000,
					srcMAC);
			}

			// and store the result
			if(workPhase==__wptDiscovery){
				addr = pack->fullAddress;
				if(addr!=NULL)
					addr->alive = true;
			}
			else{
				mach = pack->machineInfo;
				if(mach!=NULL){
					mpi.port         = ntohs(tcph->SourcePort);
					mpi.method       = myType;
					mpi.timeInterval = ComputeDeltaTime(pack->sTime, GetTickCount());
					mpi.dataSize     = 0;
					mach->AddPortInfo(&mpi);
				}
			}

			pack->Initialize(false);
		}
		return;
	}
}

//--------------------------------------------------------------------
